/**
 * @fileoverview
 *
 * main listener for data to save
 *
 * TODO: If there will be need to speed up things it can be reasonable to use Cluster
 */

var connect = require('connect'); /** tested with v.2.0.2 @link https://github.com/senchalabs/connect */
var http = require('http'); /** @link http://nodejs.org/api/http.html */
var crypto = require('crypto'); /** @link http://nodejs.org/api/crypto.html */
//var _ = require('underscore')._; /** @link http://documentcloud.github.com/underscore/ */
var utl = require('./utils.js');

//load configuration from file
var config = require('./config.json') || {};

config = utl.defaults(config, {
	'debug': false,
	'savepath': '/save',
  'domain': 'localhost',
	//'key': '',
	'port': 8081,
	'interfaces': null,
	'log': {
		'log-format': ':date | :response-time | :status | :method | :url | :referrer | :req-clientTime | :req-serverTime | :req-hash | << :req-data >>',
		'log-filename': 'listener.log'
	},
	'data_size_limit': '1mb',
	'static_enabled': false,
	'fixed': { 'path': '/check', 'response': 'OK', 'headers': {'Content-Type': 'text/plain'} },
	//'datastore': null
});
var NDEBUG = ! config['debug'];

NDEBUG || console.log('Current configuration : %j', config);

var savepath = config['savepath'];

var domain = config['domain'];
if (!domain || (domain.length == 0)) {
	console.log('Required config parameter "domain" undefined or empty.');
	process.exit(1);
}

var key = config['key'];
if (!key || (key.length == 0)) {
	console.log('Required config parameter "key" undefined or empty.');
	process.exit(1);
}

var app = connect().use(savepath, function(req,res,next){
	NDEBUG || console.log('req arrived url: ' + savepath + req.url);
	if (!req._serverTime) req._serverTime = new Date().getTime();
	NDEBUG || console.log('serverTime: ' + req._serverTime);
	
	next();
});

var data_size_limit = config['data_size_limit'];
if (data_size_limit) app.use(connect.limit(data_size_limit));

if (config['static_enabled']) {
	var static_path = __dirname + '/public/';
	console.log('static content enabled - document root serving from path: ' + static_path);
	app.use('/',connect.static(static_path, { maxAge: 0, getOnly: true }));
}

//parse request: set body (for POST) and params (for GET)
app.use(connect.urlencoded()).use(connect.query());

if (config['log']) {
	var format = config['log']['log-format'];
	if (format) {
		connect.logger.token('req-hash', function(req, res) {	return getReqPrm(req, 'hash'); });
		connect.logger.token('req-data', function(req, res) {	return getReqPrm(req, 'data'); });
		connect.logger.token('req-clientTime', function(req, res) {	return getReqPrm(req, 'clientTime'); });
		connect.logger.token('req-serverTime', function(req, res) {	return req._serverTime; });
		var options = {'format': format};
		var filename = config['log-filename'];
		if (filename) {
			var fs = require('fs');
			options['stream'] = fs.createWriteStream(filename, {flags: 'a'});
		}
		app.use(connect.logger(options));	
	}
}

var fixed = config['fixed'];
if (fixed) app.use(fixed.path, function (req, res) {
	utl.each(fixed.headers, res.setHeader, res);
	res.setHeader('Content-Length', fixed.response.length);
  res.end(fixed.response);
});


// NA TYM SIE WZOROWAC Z INCLUDE PARSER.JS
var datastore_cfg = config['datastore'];
var datastore = null;
if (datastore_cfg) {
	var DataStore = require('./store.js');
	NDEBUG || console.log('DataStore cfg: ' + JSON.stringify(datastore_cfg));
	datastore_cfg['ndebug'] = NDEBUG;
	app.use(savepath, save2store);
	var datastore = new DataStore(datastore_cfg, ready);
} else {
	ready();
}
function respond(res, body, next) {
	utl.each({
			'Content-Type': 'text/plain',
			'Content-Length': body.length
		},
		res.setHeader,
		res
	);
  res.end(body);
	NDEBUG || console.log('Response: %j', res);
};
function save2store(req, res, next) {
	NDEBUG || console.log('Request Method: %s\nRequest Url: %s\nRequest Query: %j\nRequest headers: %j\nRequest Body: %j',  req.method, savepath + req.url, req.query, req.headers, req.body);

  if (req.method == 'OPTIONS') {
		//CORS implementation
		utl.each({
			  'Access-Control-Allow-Origin': req.headers['origin'] || '',
				'Access-Control-Allow-Methods': 'POST, GET, OPTIONS', //res.headers['access-control-request-method'],
				'Access-Control-Allow-Headers': req.headers['access-control-request-headers'] || '',
				'Access-Control-Max-Age': 1728000,
			},
			res.setHeader,
			res
		);
		respond(res, '1', next);
    return;
  }
	
	if (!req._serverTime) req._serverTime = new Date().getTime();
	NDEBUG || console.log('serverTime: ' + req._serverTime);

	var data = getReqPrm(req,'data');
	var hash = getReqPrm(req,'hash');

  //source verification
	var shasum = crypto.createHash('sha1');
	shasum.update(domain);
	shasum.update(data);
	shasum.update(key);
	shasum = shasum.digest('hex');
	NDEBUG || console.log('calculated data checksum: ' + shasum);
  if (hash != shasum) {
		respond(res, '0', next);
	} else {
		var timeOffset = 0;
		var clientTime = getReqPrm(req,'clientTime');
		clientTime = clientTime || req._serverTime;
		timeOffset = req._serverTime - clientTime;
		
		respond(res, datastore.save(data, timeOffset, req._serverTime, clientTime), next);
	}
};

function getReqPrm(req, name) {
	if ('POST'==req.method) {
		if (req.body && req.body[name]) return req.body[name];
		if (req.query && req.query[name]) return req.query[name];
	} else { //GET
		if (req.query && req.query[name]) return req.query[name];
	}
	return null;
}

function ready() {
	//start listener(s)
	var port = config['port'];
	var interfaces = config['interfaces'];
	if (interfaces && interfaces.length) {
		interfaces.forEach(function (el, idx, arr) {
			var server = http.Server(app).listen(port,el,function(){
				console.log('Server listening at http://'+el+':'+port+'/');
			});
		});
	} else {
		var server = http.Server(app).listen(port,function() {
			console.log('Server listening at port '+port+'.');
		});
	}
	console.log('Listener initialization done.');
};


